/** @file    WidgetQMenu.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WidgetQMenu.cpp 2878 2010-08-09 01:11:12Z alex $
 *  @brief
 * File containing methods for the wosh::gui::WidgetQMenu class.
 * The header for this class can be found in WidgetQMenu.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/gui/components/WidgetQMenu.h>

 #include <core/ObjectAllocator.h>
 #include <framework/gui/GuiUtilities.h>
 
 #include <core/MethodsCommon.h>
 #include <core/Request.h>
 #include <core/Response.h>

 #include <QTimer>
 #include <QDateTime>
 #include <QEvent>
 #include <QApplication>


using namespace std;
namespace wosh {
 namespace gui {

 WOSH_REGISTER(wosh::gui::WidgetQMenu, "wosh::gui::IWidget", 1.0, _static_WidgetQMenu )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WidgetQMenu::WidgetQMenu( QWidget *parent ) : QMenu(parent), IWidget()
 {
//	this->setObjectName("WidgetQMenu");

	this->requestMethod = _METHOD_List;
	this->requestArgument = NULL;
	this->requestTimeOutSec = 20;

	this->joinSources = false;

	this->defaultType = "wosh::Object";
	this->forceType = false;
	this->showItemSeparator = true;

	this->getTargetURI().setKernelLocal();
	this->getWidgetURI().setKernelLocal();
	this->getWidgetURI().setName( Utilities::format("WidgetQMenu#%10lu", Utilities::randomLong()) );

	this->actionRefresh = new QAction( tr("Initialized"), this );
	this->actionRefresh->setVisible(true);
	this->actionRefresh->setEnabled(true);
	QIcon iconRefresh;
	iconRefresh.addPixmap(QPixmap(QString::fromUtf8(":/icons/var/extras/icons/menu.refresh.png")), QIcon::Normal, QIcon::Off);
	this->actionRefresh->setIcon(iconRefresh);
	QMenu::addAction(this->actionRefresh);

	this->actionNoContents = new QAction( tr("No Items"), this );
	this->actionNoContents->setVisible(false);
	this->actionNoContents->setEnabled(false);
	QIcon iconNoContents;
	iconNoContents.addPixmap(QPixmap(QString::fromUtf8(":/icons/var/extras/icons/menu.noitems.png")), QIcon::Normal, QIcon::Off);
	this->actionNoContents->setIcon(iconNoContents);
	QMenu::addAction(this->actionNoContents);

	connect(this->actionRefresh, SIGNAL(triggered()), this, SLOT(refreshAll()));
	connect(this, SIGNAL( triggered( QAction* ) ), this, SLOT(event_triggered( QAction* )));

	clearDynamics();
	init();
 }

WidgetQMenu::~WidgetQMenu() {
	if ( this->requestArgument != NULL )
		delete this->requestArgument;
 }

void WidgetQMenu::changeEvent(QEvent *e) {
	QWidget::changeEvent(e);
	if ( e->type() != QEvent::LanguageChange )
		return;
	this->actionRefresh->setText( tr("&Refresh") );
	this->actionNoContents->setText( tr("No Items") );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::event_triggered( QAction* action ) {
	 if ( !this->actionDynamic.contains(reinterpret_cast<WidgetQMenuItem*>(action)) ) return;
	 emit triggeredItem(dynamic_cast<WidgetQMenuItem*>(action));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::refreshAll() {
	this->actionNoContents->setVisible(false);
	this->actionRefresh->setText( tr("Retrieving..") );
	this->actionRefresh->setStatusTip( tr("Retrieving at %1..").arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")) );
	ISerializable* arg = NULL;
	if ( this->requestArgument != NULL )
		arg = this->requestArgument->clone();
	Request* request_items = new Request( this->requestMethod, arg );
	Message* message = new Message(request_items);
	this->postMessageForTarget(message);

	if ( this->requestTimeOutSec != 0 )
		QTimer::singleShot(this->requestTimeOutSec * 1000, this, SLOT(request_timedout()));
 }

 void WidgetQMenu::request_timedout() {
	if ( this->actionRefresh->text() != tr("Retrieving..") ) return;
	this->actionRefresh->setText( tr("&Refresh [TimedOut]") );
	this->actionRefresh->setStatusTip( tr("[FAILED] Request Timeout at %1").arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")) );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::clear() {
	this->actionDynamic.clear();
	QMenu::clear();
 }

void WidgetQMenu::clearDynamics() {
	for(int i=0; i<this->actionDynamic.size(); i++ ) {
		if ( this->actionDynamic.at(i) == NULL ) continue;
		this->removeAction(this->actionDynamic.at(i));
	 }
	this->actionDynamic.clear();
	if ( this->showItemSeparator )
		QMenu::addSeparator();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::addSeparator() {
	QMenu::insertSeparator(this->actionRefresh);
 }

void WidgetQMenu::addAction( QAction* action ) {
	QMenu::insertAction(this->actionRefresh, action);
 }

void WidgetQMenu::addActionDynamic( WidgetQMenuItem* action ) {
	this->actionDynamic.append(action);
	QMenu::addAction(action);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::setParsingData( bool value ) {
	if ( value ) {
		this->actionNoContents->setVisible(false);
		this->actionRefresh->setText( tr("Parsing at %1..").arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")) );
	}
	else {
		this->actionRefresh->setText( tr("&Refresh") );
		this->actionRefresh->setStatusTip( tr("[SUCCEEDED] Updated at %1").arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")) );
		emit updated();
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::parseTable( const wosh::Table* table ) {
	setParsingData(true);
	clearDynamics();
	if ( table == NULL || table->getRows() == 0 ) {
		this->actionNoContents->setVisible(true);
		setParsingData(false);
		return;
	 }
	unsigned int rows = table->getRows();
	int colNameIndex = table->findColumnOfItem(-1, "Name" );
	int colTypeIndex = table->findColumnOfItem(-1, "Type" );
	int colInterfacesIndex = table->findColumnOfItem(-1, "Interfaces" );
	int colURIIndex = table->findColumnOfItem(-1, "URI" );

	this->actionNoContents->setVisible(false);
	if ( colNameIndex == -1 && colURIIndex == -1 ) {
		setParsingData(false);
		return;
	 }

	for (unsigned int row=0; row<rows; row++) {
		WidgetQMenuItem* ac = new WidgetQMenuItem( this );
		if ( colNameIndex != -1) {
			const ISerializable* obj = table->get(row, colNameIndex);
			if ( obj == NULL || !obj->isKindOf<Variant>() ) {
				delete ac;
				continue;
			 }
			ac->setRemoteName(obj->as<Variant>()->toString());
		 }
		else if ( colURIIndex != -1) {
			const ISerializable* obj = table->get(row, colURIIndex);
			if ( obj != NULL && obj->isKindOf<Variant>() )
				ac->setRemoteName( URI(obj->toString()).getName() );
			else if ( obj != NULL && obj->isKindOf<URI>() )
				ac->setRemoteName(obj->as<URI>()->getName());
			else {
				delete ac;
				continue;
			 }
		 }

		addActionDynamic(ac);
		if ( this->forceType || colTypeIndex == -1 )
			ac->setRemoteType(this->defaultType);
		else {
			const ISerializable* objType = table->get(row, colTypeIndex);
			if ( objType != NULL && objType->isKindOf<Variant>() ) {
				ac->setRemoteType(objType->as<Variant>()->toString());
			}
		 }
		if ( colInterfacesIndex != -1 ) {
			const ISerializable* objInterfaces = table->get(row, colInterfacesIndex);
			if ( objInterfaces != NULL && objInterfaces->isKindOf<List>() ) {
				const wosh::List* dataInterfaces = dynamic_cast<const wosh::List*>(objInterfaces);
				ac->getInterfaces().clear();
				for(int j=0; j<dataInterfaces->size(); j++) {
					const Variant* vData = dynamic_cast<const Variant*>(dataInterfaces->itemAt(j));
					if ( vData == NULL ) continue;
					ac->getInterfaces().append(vData->toString().c_str());
				 }
				if ( colTypeIndex == -1 )
					ac->setRemoteType( dataInterfaces->itemAt(dataInterfaces->size()-1)->toString().c_str() );
			 }
			else if ( objInterfaces != NULL && objInterfaces->isKindOf<Variant>() ) {
				const wosh::Variant* dataInterfaces = dynamic_cast<const wosh::Variant*>(objInterfaces);
				ac->getInterfaces().clear();
				vector<string> inters;
				Utilities::splitString(	dataInterfaces->toString(), ";", inters, false, true);
				for(unsigned int j=0; j<inters.size(); j++) {
					ac->getInterfaces().append(inters.at(j).c_str());
				 }
				if ( colTypeIndex == -1 )
					ac->setRemoteType( inters.at(inters.size()-1).c_str() );
			 }
		 }
		if ( colURIIndex != -1 ) {
			const ISerializable* objURI = table->get(row, colURIIndex);
			if ( objURI != NULL && objURI->isKindOf<Variant>() ) {
				const wosh::Variant* dataURI = dynamic_cast<const wosh::Variant*>(objURI);
				if ( dataURI  != NULL )
					ac->getRemoteURI().fromString(dataURI->toString());
			}
			else if ( objURI != NULL && objURI->isKindOf<URI>() ) {
				const wosh::URI* uriURI = dynamic_cast<const wosh::URI*>(objURI);
				if ( uriURI  != NULL )
					ac->setRemoteURI( *uriURI );
			}
		 }
		else { // unknown uri, try to crate uri as child of our target
			ac->getRemoteURI().setParent(this->targetURI);
			ac->getRemoteURI().setName(ac->getRemoteName());
		 }
		ac->updateStatusTip();
	}
	setParsingData(false);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::parseList( const wosh::List* dList ) {
	setParsingData(true);
	clearDynamics();
	if ( dList == NULL || dList->size() == 0 ) {
		this->actionNoContents->setVisible(true);
		setParsingData(false);
		return;
	 }
	std::string kName = GuiSession::getInstance().getCurrentKernel();
	for (int row=0; row<dList->size(); row++) {
		if ( dList->itemAt(row)->toString() == kName )
			continue;
		WidgetQMenuItem* ac = new WidgetQMenuItem( this );
		ac->setRemoteName(dList->itemAt(row)->toString());
		ac->setRemoteType(this->defaultType);
		addActionDynamic(ac);
	 }
	setParsingData(false);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::parse( const ISerializable* obj ) {
	if ( obj == NULL ) return;
	if ( obj->isKindOf<Table>() ) {
		const wosh::Table* dTable = obj->as<Table>();
		parseTable(dTable);
	 }
	else if ( obj->isKindOf<List>() ) { 
		const wosh::List* dList = dynamic_cast<const wosh::List*>(obj);
		parseList(dList);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetQMenu::busMessage( const Message& message, const Bus* ) {
	if ( message.isEmpty() || !message.getContent()->isResponse() ) return;
	if ( !isMessageForMeOrFromTarget(message) ) return;
	const wosh::Response* response = message.getContent()->asResponse();
	if ( response->getMethod() != this->requestMethod ) return;
	if ( WFAILED(response->getReturnValue()) ) {
		this->actionRefresh->setText( tr("&Refresh [Failed#%1]").arg(response->getReturnValue()) );
		if ( response->hasData() )
			this->actionRefresh->setStatusTip( tr("[FAILED] Error#%1 Response at %2: %3 [%4]")
												.arg(response->getReturnValue()).arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz"))
												.arg(Method::getGenericErrorDescription(response->getReturnValue())).arg(response->getData()->toString().c_str()) );
		else 
			this->actionRefresh->setStatusTip( tr("[FAILED] Error#%1 Response at %2: %3")
												.arg(response->getReturnValue()).arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz"))
												.arg(Method::getGenericErrorDescription(response->getReturnValue())) );
		return;
	 }
	if ( !response->hasData() ) {
		this->actionRefresh->setText( tr("&Refresh [Empty]") );
		this->actionRefresh->setStatusTip( tr("[FAILED] Empty Response at %1").arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")) );
		return;
	 }
	if ( response->getMethod() == this->requestMethod )
		parse(response->getData());
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
